using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Windows.Forms;
using System.Reflection;
using System.Drawing;
using System.Diagnostics;
using System.Data;
using System.ComponentModel.Design.Serialization;
using System.CodeDom;
using GuavaData;

namespace GuavaLib
{
    [DesignerSerializer(typeof(GLaunchControlSerializer<GCheckFlag>),
        typeof(CodeDomSerializer))]
    public class GCheckFlag : System.Windows.Forms.Label, Guava, GuavaLaunchBase
	{
        string gname;
        string strLaunchFile;
        Type s;
        
        public const int ciCheckFlagHeight = 0x10;
	
		// === checkReq static resources
		private static Pen redPen;
		private static Pen blackPen;
		private static Pen whitePen;
		private static SolidBrush greenBrush;
		private static SolidBrush blackBrush;
		private static SolidBrush chocoBrush;
		private static SolidBrush whiteBrush;
		private static SolidBrush grayBrush;
		private static Rectangle ricon;
		private static Rectangle dicon;

		// state variables
		private bool m_subdata;
		private byte m_ckstate;
		private bool m_checked;
		private	bool m_bIsEnabled;
		private bool mousehit;

		private Point[] ps = new Point[4];

		private Color		clrSaveForeColor;
		private SolidBrush	CheckboxTextBrush;

        public event EventHandler DataStateChanged;

        [Browsable(false)]
        public bool SubData
        {
            get { return m_subdata; }
            set
            {
                m_subdata = value;
                if (m_subdata) DataState = 2;
                else if (m_ckstate == 2) DataState = 1;
            }
        }

        [Browsable(false), DefaultValue((byte)0)]
        public byte DataState
		{
            get
            {
                return m_ckstate;
            }
			set 
			{
                if (m_ckstate != value)
                {
                    m_ckstate = value;
                    m_checked = (value > 0);
                    Refresh();
                    OnDataStateChanged(new CheckFlagCheckChangedArgs(value));
                }
			} 
		}

        public void OnDataStateChanged(CheckFlagCheckChangedArgs args)
        {
            if (DataStateChanged != null)
            {
                DataStateChanged(this, args);
                foreach (Binding b in this.DataBindings) b.WriteValue();
            }
        }

        [Browsable(false)]
        public bool Checked
		{ 
			get { return m_checked; } 
		}

        public GCheckFlag()
		{
            AutoSize = false;
            m_ckstate = 0;
			m_bIsEnabled = true;
			clrSaveForeColor = this.ForeColor;
			FlatStyle = System.Windows.Forms.FlatStyle.Flat;
			if (redPen == null)  // if this is the first instance
			{
				redPen = new Pen(Color.Red);
				redPen.Width = 2;
				blackPen   = new Pen(Color.Black);
				whitePen   = new Pen(Color.White);
				greenBrush = new SolidBrush(Color.Green);
				blackBrush = new SolidBrush(Color.Black);
				chocoBrush = new SolidBrush(System.Drawing.Color.Chocolate);
				whiteBrush = new SolidBrush(Color.White);
				grayBrush = new SolidBrush(Color.Gray);
				ricon = new Rectangle(2, 2, 14, 10);
				dicon = new Rectangle(3, 3, 8, 8);
			}
			CheckboxTextBrush = blackBrush;
            this.Size = new System.Drawing.Size(this.PreferredWidth + 30, ciCheckFlagHeight);
		}

		protected override void OnClick(EventArgs e)
		{
			if (! m_bIsEnabled)
				return;
			//-----------------------------------------------------------------
			// if the box was not clicked then just show the popup if available
            if (!mousehit)
            {
                LaunchForm();
                Invalidate();
                return;
            }

			// if it was checked and subform had data
			if (Checked && DataState == 2)		
			{
                LaunchForm();
				Invalidate();
				return;
			}
			if (Checked)
			{
				DataState = 0;
				Invalidate();
				return;
			}
            if (this.GLaunchType == null) // if there is no sub data then just check it
            {
                DataState = 1;
                Invalidate();
                return;
            }

			// clicked on unchecked box and have sub form
			DataState = 1;	// initially just check the box
			LaunchForm();
			Invalidate();
			return;
		}

		protected override void OnMouseDown(System.Windows.Forms.MouseEventArgs e)
		{
			base.OnMouseDown(e);
			mousehit = ricon.Contains(e.X, e.Y);
		}

		#region Paint

		private void drawCheck(Graphics g, Pen p)
		{
			int ax = ricon.Left + 3;
			int ay = ricon.Top + 3;
			int bx = ricon.Left + 5;
			int by = ricon.Top + 8;
			int cx = ricon.Left + 12;
			int cy = ay;
			p.Width = 2;
			g.DrawLine(p, ax, ay, bx, by);
			g.DrawLine(p, bx, by, cx, cy);
		}

		private void drawCheckboxState(Graphics g)
		{
			switch (m_ckstate)
			{
				case 0:
					g.FillRectangle(whiteBrush, ricon);
					blackPen.Width = 1;
					g.DrawRectangle(blackPen, ricon);
					break;
				case 1:
					g.FillRectangle(whiteBrush, ricon);
					blackPen.Width = 1;
					g.DrawRectangle(blackPen, ricon);
					drawCheck(g, blackPen);
					break;
				case 2:
					g.FillRectangle(chocoBrush, ricon);
					blackPen.Width = 1;
					g.DrawRectangle(blackPen, ricon);
					drawCheck(g, whitePen);
					break;
				default:
					Debug.Assert(false, "CheckReq unknown control state");
					break;
			} 
		}

		protected override void OnPaint(System.Windows.Forms.PaintEventArgs e)
		{
			Graphics g = e.Graphics;
			// draw the string
			SizeF stringSize = g.MeasureString(this.Text, this.Font);

		    this.BackColor = System.Drawing.SystemColors.Control;

			g.DrawString(Text, this.Font, CheckboxTextBrush, 18f, 1f);
			// draw the right icon if needed
            if (this.GLaunchType != null)
            {
                int x = (int)(stringSize.Width + 0.5f) + 18;
                ps[0].X = x;
                ps[0].Y = 0;
                ps[1].X = x;
                ps[1].Y = 15;
                ps[2].X = x + 8;
                ps[2].Y = 8;
                ps[3].X = x;
                ps[3].Y = 0;
                g.FillPolygon(chocoBrush, ps);
            }
			if (m_bIsEnabled)
				drawCheckboxState(g);
		}

		#endregion

		public bool IsEnabled
		{
			get
			{
				return m_bIsEnabled;
			}
			set
			{
				if (value)
				{
					//this.Enabled = true;
					this.ForeColor = clrSaveForeColor;
					CheckboxTextBrush = blackBrush;
				}
				else
				{
					clrSaveForeColor = this.ForeColor;
					this.ForeColor = Color.Red;
					//this.Enabled = false;
					//this.Checked = false;
					DataState = 0;
					CheckboxTextBrush = grayBrush;
				}
				m_bIsEnabled = value;
				Invalidate();
			}
		}
        
        #region Guava Members

        [Browsable(false)]
        public GuavaData.LaunchType LType
        {
            get
            {
                if (this.GLaunchType == null || this.GLaunchType == "") return GuavaData.LaunchType.NA;
                return GuavaData.LaunchType.Single;
            }
        }

        [Browsable(false)]
        public GuavaData.ControlType CType
        {
            get { return GuavaData.ControlType.Attribute; }
        }

        public string GText
        {
            get { return this.Text; }
        }

        public string GName
        {
            get
            {
                if (gname == null) return this.Name;
                return gname;
            }
            set
            {
                gname = value;
            }
        }

        [Browsable(false)]
        public Point GLocation
        {
            get { return this.Location; }
        }

        [Browsable(false)]
        public Size GSize
        {
            get { return this.Size; }
        }

        [Browsable(false)]
        public List<GuavaData.Guava> GComponents
        {
            get
            {
                List<Guava> lg = new List<Guava>();
                if (s == null) return lg;

                ConstructorInfo ci = s.GetConstructor(new Type[] { });
                Object o = ci.Invoke(new object[] { });
                lg.Add(o as Guava);

                return lg;
            }
        }

        public void GBind(string table, int i)
        {
            this.DataBindings.Clear();
            DataRow dr = AppData.ds.Tables[table].Rows[i];
            if (dr[this.GName] is DBNull) dr[this.GName] = 0;
            this.DataBindings.Add("DataState", AppData.ds.Tables[table], this.GName);
        }

        [Browsable(false)]
        public string GDefault
        {
            get { return DataState.ToString(); }
        }

        [Browsable(false)]
        public string GToolTip
        {
            get
            {
                Control p = this.TopLevelControl;
                if (!(p is Guava)) return null;
                ToolTip t = (p as GuavaForm).GToolTipProvider;
                if (t == null) return null;

                // Fetch the tooltip and return it
                return t.GetToolTip(this);
            }
        }

        [Browsable(false)]
        public GuavaData.Domain.Domain GDomain
        {
            get { return new GuavaData.Domain.TinyInteger(0, 2); }
        }

        public GuavaData.Guava this[string s]
        {
            get
            {
                foreach (Guava g in this.GComponents) if (g.GName == s) return g;
                return null;
            }
        }

        #endregion

        [Editor(typeof(GUIFilenameEditor), typeof(System.Drawing.Design.UITypeEditor))]
        public string GLaunchType
        {
            get
            {
                return strLaunchFile;
            }

            set
            {
                strLaunchFile = value;
            }
        }

        [Browsable(false)]
        public Type Launch
        {
            get
            {
                return s;
            }

            set
            {
                this.s = value;
            }
        }

        public Type GetLaunchType()
        {
            if ((null == s) && strLaunchFile != null && strLaunchFile.Length > 0)
            {
                s = GLaunchClass.RetrieveTypeFromFile(strLaunchFile, Assembly.GetCallingAssembly());
            }
            return s;
        }

        #region GuavaLaunchBase Members

        public void SetObject(Guid id)
        {
            return;
        }

        public void PrepareStackFrame()
        {
            // Build the seed stack frame call list
            GuavaForm gForm = this.TopLevelControl as GuavaForm;
            GSeed gsTemp = gForm.GS;
            GSeedFrame gsfTemp = gsTemp.Peek();

            // Create a list of controls
            Control p = this;
            Control q = p.Parent;
            List<string> ls = new List<string>();
            ls.Add(this.GName);

            // Follow parents up the chain to the root form
            while (!(q is GuavaForm))
            {
                if (!(q is Guava))
                {
                    Guava r = q.Parent as Guava;
                    if (r == null) throw new Exception("Found two non-guava objects nested together in the same tree for marking.");
                    bool bfound = false;
                    foreach (Guava g in r.GComponents)
                    {
                        if (g.GComponents.Contains(p as Guava))
                        {
                            ls.Insert(0, g.GName);
                            bfound = true;
                            break;
                        }
                    }
                    if (!bfound) throw new Exception("Could not match a guava object for creating a seed for marking.");
                }
                else ls.Insert(0, (q as Guava).GName);
                p = p.Parent;
                q = q.Parent;
            }
            gsfTemp.controls = ls;
        }

        public DialogResult LaunchForm(Guid gid, string form, bool bNew)
        {
            if (s == null) return DialogResult.Cancel;

            // Get parent form
            Control p = this.TopLevelControl;
            if (!(p is Guava)) return DialogResult.Cancel;

            ConstructorInfo ci = s.GetConstructor(new Type[] { typeof(Guid), typeof(Guid), typeof(bool), typeof(GSeed) });
            GForm o = ci.Invoke(new object[] { gid, (p as GuavaForm).ID, bNew, (p as GuavaForm).GS }) as GForm;
            DialogResult dr = o.ShowDialog();
            this.SubData = o.HasData;
            return dr;
        }

        #endregion

        private void LaunchForm()
        {
            // Get parent form
            Control p = this.TopLevelControl;
            if (!(p is Guava)) return;
            Guid g = (p as GuavaForm).ID;
            LaunchForm(g, null, false);
        }
    }
    
    public class CheckFlagCheckChangedArgs : EventArgs
    {
        bool m_checked;
        byte m_ckstate;

        public byte Ckstate
        {
            get { return m_ckstate; }
        }

        public bool Checked
        {
            get { return m_checked; }
        }

        public CheckFlagCheckChangedArgs(byte ckstate)
            : base()
        {
            this.m_checked = (ckstate > 0);
            this.m_ckstate = ckstate;
        }
    }
}
